package org.itsolutions.javaBackup.ioc;

import org.itsolutions.javaBackup.spi.EModule;
import org.itsolutions.javaBackup.spi.IModuleStartStopTask;

import com.google.inject.AbstractModule;
import com.google.inject.Provider;
import com.google.inject.binder.ScopedBindingBuilder;
import com.google.inject.multibindings.MapBinder;
import com.google.inject.multibindings.Multibinder;

/**
 * 
 * @author Tomas Fecko
 */
public abstract class AJavaBackupModule extends AbstractModule {


	/**
	 * Helper method for registering startStop task.
	 * @param pStartTaskClass start task class
	 */
	protected final void registerStartStopTask(Class<? extends IModuleStartStopTask> pStartTaskClass) {
		multibind(IModuleStartStopTask.class, pStartTaskClass);
	}

	/**
	 * Binds multiple classes to one Map<pKey, pInterfaceClass>. <br />
	 * Usage:<br />
	 * @Inject Map<Key, pInterfaceClass> pMap
	 * @param <K> key object in map
	 * @param <V> type of the interface
	 * @param pInterfaceClass interface
	 * @param pImplClass implementing class
	 * @param pKey key object in map
	 */
	@SuppressWarnings("unchecked")
	protected final <V, K> void multibind(Class<V> pInterfaceClass, Class<? extends V> pImplClass, K pKey) {
		if (pInterfaceClass != null && pImplClass != null && pKey != null) {
		MapBinder<K, V> mapBinder = MapBinder.newMapBinder(binder(), (Class<K>) pKey.getClass(), pInterfaceClass);
		mapBinder.addBinding(pKey).to(pImplClass);
		}
	}


	/**
	 * Binds multiple classes to one Map<pKey, pInterfaceClass>. <br />
	 * Usage:<br />
	 * @Inject Map<Key, pInterfaceClass> pMap
	 * @param <K> key object in map
	 * @param <V> type of the interface
	 * @param pInterfaceClass interface
	 * @param pImplClass implementing class
	 * @param pKey key object in map
	 */
	@SuppressWarnings("unchecked")
	protected final <V, K> void multibindProvider(Class<V> pInterfaceClass, Class<? extends Provider<V>> pImplClass, K pKey) {
		if (pInterfaceClass != null && pImplClass != null && pKey != null) {
		MapBinder<K, V> mapBinder = MapBinder.newMapBinder(binder(), (Class<K>) pKey.getClass(), pInterfaceClass);
		mapBinder.addBinding(pKey).toProvider(pImplClass);		
		}
	}
	
	/**
	 * Binds multiple classes to one Set.<br />
	 * Usage:<br />
	 * @Inject Set<pInterfaceClass> pSet
	 * @param <T> type of the interface
	 * @param pInterfaceClass interface
	 * @param pImplClass implementing class
	 */
	protected final <T> void multibind(Class<T> pInterfaceClass, Class<? extends T> pImplClass) {
		Multibinder<T> uriBinder = Multibinder.newSetBinder(binder(), pInterfaceClass);
		uriBinder.addBinding().to(pImplClass);
	}

	/**
	 * Method bind the interface to class with annotation with class identifier.
	 * You can than identificate the injection point with e.g.:
	 * @InjectionID(Impl.class)
	 * 
	 * @param <T> interface type
	 * @param pInterface interface to bind
	 * @param pImpl implementation of that interface
	 * @return ScopedBindingBuilder
	 */
	protected final <T> ScopedBindingBuilder bindWithID(Class<T> pInterface, Class<? extends T> pImpl) {
		return bind(pInterface).annotatedWith(new InjectionIDImpl(pImpl)).to(pImpl);
	}

	/**
	 * Method bind the interface to class with annotation with string identifier.
	 * You can than identificate the injection point with e.g.:
	 * @InjectionID(id = "someString")
	 * 
	 * @param <T> interface type
	 * @param pInterface interface to bind
	 * @param pImpl implementation of that interface
	 * @param pID string identifier
	 * @return ScopedBindingBuilder
	 */
	protected final <T> ScopedBindingBuilder bindWithID(Class<T> pInterface, Class<? extends T> pImpl, String pID) {
		return bind(pInterface).annotatedWith(new InjectionIDImpl(pID)).to(pImpl);
	}

	/**
	 * Method bind the interface to class with annotation with current module identifier.
	 * You can than identificate the injection point with e.g.:
	 * @InjectionID(module = EModule.UNSPECIFIED)
	 * 
	 * @param <T> interface type
	 * @param pInterface interface to bind
	 * @param pImpl implementation of that interface
	 * @return ScopedBindingBuilder
	 */
	protected final <T> ScopedBindingBuilder bindWithModuleID(Class<T> pInterface, Class<? extends T> pImpl) {
		return bind(pInterface).annotatedWith(new InjectionIDImpl(getModule())).to(pImpl);
	}

	/**
	 * Method bind the concrete class to class with annotation with current module identifier.
	 * You can than identificate the injection point with e.g.:
	 * @InjectionID(module = EModule.UNSPECIFIED)
	 * 
	 * @param <T> type of the concrete class
	 * @param pImpl implementation class (without interface)
	 * @return ScopedBindingBuilder
	 */
	protected final <T> ScopedBindingBuilder bindClassWithModuleID(Class<T> pImpl) {
		return bind(pImpl).annotatedWith(new InjectionIDImpl(getModule())).to(pImpl);
	}

	/**
	 * Method bind the concrete class to class with annotation with current module identifier.
	 * You can than identificate the injection point with e.g.:
	 * @InjectionID(module = EModule.UNSPECIFIED)
	 * 
	 * @param <T> type of the concrete class
	 * @param pImpl implementation class (without interface)
	 * @return ScopedBindingBuilder
	 */
	protected final <T> ScopedBindingBuilder bindClassWithID(Class<T> pImpl) {
		return bind(pImpl).annotatedWith(new InjectionIDImpl(pImpl)).to(pImpl);
	}
	
	/**
	 * 
	 * @return should return the module, to whom the module belongs
	 */
	protected abstract EModule getModule();
}




